package org.jvm.nativemethod.methods.java.io;

import org.jvm.nativemethod.NativeMethodRegister;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.classLoader.*;
import org.jvm.rtda.heap.stringpool.StringPool;
import org.jvm.rtda.thread.*;
import org.jvm.util.JvmUtil;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 海燕
 * @date 2023/2/26
 */
public class FileSystemNativeMethod extends NativeMethodRegister {

    public static void init() throws NoSuchMethodException {
        String[] names = new String[]{"java/io/UnixFileSystem", "java/io/WinNTFileSystem"};
        for (String name : names) {
            register(name, "canonicalize0", "(Ljava/lang/String;)Ljava/lang/String;", FileSystemNativeMethod.class.getDeclaredMethod("canonicalize0", Frame.class));
            register(name, "getBooleanAttributes0", "(Ljava/io/File;)I", FileSystemNativeMethod.class.getDeclaredMethod("getBooleanAttributes0", Frame.class));
            register(name, "getBooleanAttributes", "(Ljava/io/File;)I", FileSystemNativeMethod.class.getDeclaredMethod("getBooleanAttributes0", Frame.class));
            register(name, "getLastModifiedTime", "(Ljava/io/File;)J", FileSystemNativeMethod.class.getDeclaredMethod("getLastModifiedTime", Frame.class));
            register(name, "getLength", "(Ljava/io/File;)J", FileSystemNativeMethod.class.getDeclaredMethod("getLength", Frame.class));
            register(name, "list", "(Ljava/io/File;)[Ljava/lang/String;", FileSystemNativeMethod.class.getDeclaredMethod("list", Frame.class));
        }
    }

    public static void list(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object fileObj = localVars.getRef(1);
        String path = getPath(fileObj);
        File pathFile = new File(path);
        String[] list = pathFile.list();
        List<Object> collect = Arrays.stream(list)
                .map(s -> StringPool.jString(s))
                .collect(Collectors.toList());

        BootKlassLoader bootKlassLoader = KlassLoaderRegister.getBootKlassLoader();
        Object arrayObj = bootKlassLoader.loadKlass("java/lang/String").arrayClass().newArray(collect.size());
        Object[] array = (Object[]) arrayObj.getData();
        for (int i = 0; i < collect.size(); i++) {
            array[i] = collect.get(i);
        }
        frame.getOperandStack().pushRef(arrayObj);
    }


    public static void canonicalize0(Frame frame) {

        LocalVars localVars = frame.getLocalVars();
        Object pathStr = localVars.getRef(1);
        String path = JvmUtil.javaStringToJvmString(pathStr);
        String canonicalPath;
        try {
            canonicalPath = new File(path).getCanonicalPath();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if (path.equals(canonicalPath)) {
            frame.getOperandStack().pushRef(pathStr);
        } else {
            frame.getOperandStack().pushRef(StringPool.jString(canonicalPath));
        }
    }

    public static void getBooleanAttributes0(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object fileObj = localVars.getRef(1);
        String path = getPath(fileObj);
        File pathFile = new File(path);
        int attributes0 = 0;
        if (pathFile.exists()) {
            attributes0 |= 0x01;
        }
        if (pathFile.isDirectory()) {
            attributes0 |= 0x04;
        }
        frame.getOperandStack().pushInt(attributes0);
    }

    public static void getLastModifiedTime(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object fileObj = localVars.getRef(1);
        String path = getPath(fileObj);
        File pathFile = new File(path);
        frame.getOperandStack().pushLong(pathFile.lastModified());
    }

    public static void getLength(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object fileObj = localVars.getRef(1);
        String path = getPath(fileObj);
        File pathFile = new File(path);
        frame.getOperandStack().pushLong(pathFile.length());
    }

    private static String getPath(Object fileObj) {
        Object pathStr = fileObj.getRefVar("path", "Ljava/lang/String;");
        return JvmUtil.javaStringToJvmString(pathStr);
    }

}
